/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package src;

import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

interface Buffer<T> {
    void put(T message);
    T take();
}

class CircleBuffer implements Buffer<String> {
    final int n;
    String[] messages;
    int insert = -1;
    int delete = 0;
    int filled = 0;
    
    public CircleBuffer(int n) {
        this.n = n;
        messages = new String[n];
    }
    
    @Override
    public synchronized void put(String message) {        
        while (filled == n) {
            try {
                wait();
            } catch (InterruptedException ex) {}
        }
        insert = (insert + 1) % n;
        messages[insert] = message;
        filled++;
        notifyAll();
    }
    
    @Override
    public synchronized String take() {
        while (filled == 0) {
            try {
                wait();
            } catch(InterruptedException ex) {}
        }
        String message = messages[delete];
        messages[delete] = null;
        delete = (delete + 1) % n;
        filled--;
        notifyAll();
        return message;        
    }    
}

class Producer implements Runnable {

    private Buffer<String> buffer;
    
    private static final String[] messages = {"aaaa", "bbbb", "cccc", "dddd", "eeee",
    "ffff", "gggg", "hhhh", "iiii", "jjjj", "kkkk", "llll", "mmmm", "nnnn", "oooo"};
        
    Producer(Buffer<String> buffer) {
        this.buffer = buffer;
    }
    
    @Override
    public void run() { 
        Random random = new Random();
        while(true) {
            String message = messages[random.nextInt(messages.length)];
            buffer.put(message);
            System.out.println("Producer" + Thread.currentThread().getName() + ": sended '" + message + "'");
            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException ex) {}
        }                     
    }
}

class Consumer implements Runnable {

    private Buffer<String> buffer;
    
    Consumer(Buffer<String> buffer) {
        this.buffer = buffer;
    }
    
    @Override
    public void run() {
        Random random = new Random();
        while (true) {
            String message = buffer.take();
            System.out.println("Consumer" + Thread.currentThread().getName() + ": received '" + message + "'");
            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException ex) {}
        }
    }    
}

/**
 *
 * @author Oksana_Moroz
 */
public class Pr3_1 {
    
    public static void main(String[] args) {
        Buffer<String> buffer = new CircleBuffer(4);
        Thread[] threads = new Thread[5];
        threads[0] = new Thread(new Producer(buffer));
        threads[1] = new Thread(new Producer(buffer));
        threads[2] = new Thread(new Consumer(buffer));
        threads[3] = new Thread(new Consumer(buffer));
        threads[4] = new Thread(new Consumer(buffer));
        
        for(int i = 0; i < threads.length; i++) {
            threads[i].start();
        }
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException ex) {
                Logger.getLogger(Pr3_1.class.getName()).log(Level.SEVERE, null, ex);
            }
        }        
    }
}
